﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows.Forms;
namespace lox_z12Class
{
    class Resolver : Expr.Visitor<Object>, Stmt.Visitor<string>
    {
        private Interpreter interpreter;
        //一个栈帧，随着block而 不断弹出，压入。  就是当前 作用域列表。 从当前层，到最底层是 全局作用域。
        private Stack<Dictionary<string, bool>> scopes = new Stack<Dictionary<string, bool>>();
        private FunctionType currentFunction = FunctionType.NONE;

        public Resolver(Interpreter interpreter) { this.interpreter = interpreter; }
        private enum FunctionType { NONE, FUNCTION, INITIALIZER, METHOD }
        private enum ClassType { NONE, CLASS }

        private ClassType currentClass = ClassType.NONE;
        public void resolve(List<Stmt> statements)
        {
            foreach (Stmt statement in statements) {
                resolve(statement);
            }
        }
        public string visitBlockStmt(Stmt.Block stmt)
        {
            beginScope();
            resolve(stmt.statements);
            endScope();
            return null;
        }
        public string visitClassStmt(Stmt.Class stmt)
        {
            ClassType enclosingClass = currentClass;
            currentClass = ClassType.CLASS;

            declare(stmt.name);
            define(stmt.name);

            beginScope();
            scopes.Peek()["this"]= true;

            foreach (Stmt.Function method in stmt.methods) {
                FunctionType declaration = FunctionType.METHOD;
                if (method.name.lexeme=="init") {
                    declaration = FunctionType.INITIALIZER;
                }

                resolveFunction(method, declaration); // [local]
            }

            endScope();

            currentClass = enclosingClass;
            return null;
        }
        public string visitExpressionStmt(Stmt.Expression stmt)
        {
            resolve(stmt.expression);
            return null;
        }
        public string visitFunctionStmt(Stmt.Function stmt)
        {
            declare(stmt.name);
            define(stmt.name);

            resolveFunction(stmt, FunctionType.FUNCTION);
            return null;
        }
        public string visitIfStmt(Stmt.If stmt)
        {
            resolve(stmt.condition);
            resolve(stmt.thenBranch);
            if (stmt.elseBranch != null)
                resolve(stmt.elseBranch);
            return null;
        }
        public string visitPrintStmt(Stmt.Print stmt)
        {
            resolve(stmt.expression);
            return null;
        }
        public string visitReturnStmt(Stmt.Return stmt)
        {
            if (currentFunction == FunctionType.NONE) {
                MessageBox.Show(stmt.keyword+"     Can't return from top-level code.");
            }

            if (stmt.value != null) {
                if (currentFunction == FunctionType.INITIALIZER) {
                MessageBox.Show(stmt.keyword+ "Can't return a value from an initializer.");
            }
                resolve(stmt.value);
            }

            return null;
        }
        public string visitVarStmt(Stmt.Var stmt)
        {
            declare(stmt.name);
            if (stmt.initializer != null) {
                resolve(stmt.initializer);
            }
            define(stmt.name);
            return null;
        }
        public string visitWhileStmt(Stmt.While stmt)
        {
            resolve(stmt.condition);
            resolve(stmt.body);
            return null;
        }
        public Object visitAssignExpr(Expr.Assign expr)
        {
            resolve(expr.value);
            resolveLocal(expr, expr.name);
            return null;
        }
        public Object visitBinaryExpr(Expr.Binary expr)
        {
            resolve(expr.left);
            resolve(expr.right);
            return null;
        }
        public Object visitCallExpr(Expr.Call expr)
        {
            resolve(expr.callee);

            foreach (Expr argument in expr.arguments) {
                resolve(argument);
            }

            return null;
        }
        public Object visitGetExpr(Expr.Get expr)
        {
            resolve(expr._object);
            return null;
        }
        public Object visitGroupingExpr(Expr.Grouping expr)
        {
            resolve(expr.expression);
            return null;
        }
        public Object visitLiteralExpr(Expr.Literal expr) { return null; }
        public Object visitLogicalExpr(Expr.Logical expr)
        {
            resolve(expr.left);
            resolve(expr.right);
            return null;
        }
        public Object visitSetExpr(Expr.Set expr)
        {
            resolve(expr.value);
            resolve(expr._object);
            return null;
        }
        public Object visitThisExpr(Expr.This expr)
        {
            if (currentClass == ClassType.NONE) {
                MessageBox.Show(expr.keyword+ "Can't use 'this' outside of a class.");
                return null;
            }

            resolveLocal(expr, expr.keyword);
            return null;
        }
        public Object visitUnaryExpr(Expr.Unary expr)
        {
            resolve(expr.right);
            return null;
        }
        public Object visitVariableExpr(Expr.Variable expr)
        {
            //if (!scopes.isEmpty() && scopes.peek().get(expr.name.lexeme) == Boolean.FALSE) {
            //    Lox.error(expr.name, "Can't read local variable in its own initializer.");
            //}
            //if (!(scopes.Count == 0) && scopes.Peek()[expr.name.lexeme] == false)
            //{
            //    MessageBox.Show(expr.name + "      Can't read local variable in its own initializer.");
            //}

            resolveLocal(expr, expr.name);
            return null;
        }
        private void resolve(Stmt stmt) { stmt.accept(this); }
        private void resolve(Expr expr) { expr.accept(this); }
        private void resolveFunction(Stmt.Function function, FunctionType type)
        {
            FunctionType enclosingFunction = currentFunction;
            currentFunction = type;

            beginScope();
            foreach (Token param in function._params) {
                declare(param);
                define(param);
            }
            resolve(function.body);
            endScope();
            currentFunction = enclosingFunction;
        }
        private void beginScope() { scopes.Push(new Dictionary<string, bool>()); }
        private void endScope() { scopes.Pop(); }
        private void declare(Token name)
        {
            //if (scopes.isEmpty())
            if (scopes.Count ==0)
                return;

            Dictionary<String, Boolean> scope = scopes.Peek();
            if (scope.ContainsKey(name.lexeme)) {
                MessageBox.Show(name+ "     Already a variable with this name in this scope.");
            }
            
            scope.Add(name.lexeme, false);
        }
        private void define(Token name)
        {
            if (scopes.Count==0)
                return;

            //java的 Map，相同key，value是覆盖的，而 c#的是报错的。但是 c#的字典，赋值，如果不存在key，就会自动添加一个。
            scopes.Peek()[name.lexeme] = true;
        }
        private void resolveLocal(Expr expr, Token name)
        {
            //for (int i = scopes.Count - 1; i >= 0; i--)
            //{
            //    if (scopes.get(i).containsKey(name.lexeme)) {
            //        interpreter.resolve(expr, scopes.size() - 1 - i);
            //        return;
            //    }
            //}

            int i = scopes.Count - 1;
            foreach (Dictionary<string, bool> dic in scopes)
            {
                if (dic.ContainsKey(name.lexeme))
                {
                    interpreter.resolve(expr, scopes.Count - 1 - i);
                    return;
                }
                i--;
            }
        }
    }
}
